
#ifndef __WEBSERVER__
#define __WEBSERVER__

#include <iostream>
#include <unordered_map>
#include <unistd.h>
#include <thread>
#include <signal.h>
#include <functional>
#include <sys/socket.h>
#include <mutex>

#include "Entrance.hpp"
#include "ThreadPool.hpp"
#include "TcpSocket.hpp"
#include "MyTimefd.hpp"

#define QUEUE_TASK_NUM 3      // 任务队列最大数
#define EPOLL_EVENT_MAXNUM 10 // epoll事件就绪队列单次最大量

#define FIRST_TIMEOUT 10 // 首次定时器时间
#define TIMEOUT 10       // 定时时间

// 增加单例模式
// template <class THREAD_TASK>
class webserver
{
    typedef ThreadPool<Thread_Task *> thread_pool;
    typedef std::function<void(ConnectData *)> fd_func;

private:
    int _port = -3;
    int _server_listen_socket = -3;
    thread_pool *st = nullptr;

    int _efd;
    struct epoll_event *_epoll_event_array; // 事件就绪缓冲区
    size_t max_num;                         // 最大事件就绪量

    std::unordered_map<size_t, ConnectData *> fd_dispath;

    // 单例模式
    static webserver *static_webserver;
    static std::mutex _mutex;

    webserver()
    {
        signal(SIGPIPE, SIG_IGN);
        // 防止send，向关闭管道写入，导致webserver崩溃
    }

public:
    webserver(const webserver &it) = delete;
    webserver &operator=(const webserver &it) = delete;

    static webserver *GetInstace()
    {
        if (static_webserver == nullptr)
        {
            _mutex.lock();
            if (static_webserver == nullptr) // 防止都进行了判断
                static_webserver = new webserver();
            _mutex.unlock();
        }
        if (static_webserver)
            Logmessage(INFO, "server 初始化成功");
        return static_webserver;
    }

    ~webserver()
    {
        if (_server_listen_socket > 0)
            close(_server_listen_socket);
        if (_efd > 0)
            close(_efd);
        if (_epoll_event_array != nullptr)
            delete[] _epoll_event_array;
    }

    int GetEfd() { return _efd; }

    void initial(int _port, thread_pool *th_pool)
    {
        _epoll_event_array = new epoll_event[EPOLL_EVENT_MAXNUM];
        if (_epoll_event_array == nullptr)
        {
            Logmessage(ERROR, "event array new fail");
            exit(3);
        }

        max_num = EPOLL_EVENT_MAXNUM;

        MySocket tool;
        _server_listen_socket = tool._socket_set();

        // 设置复用端口号,防止崩溃后无法立即重启
        int tmp = 1;
        if (setsockopt(_server_listen_socket, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) < 0)
            std::cout << "setsockopt fail" << std::endl;

        tool.bind_set(_server_listen_socket, _port);
        Logmessage(INFO, "服务端初始化成功");

        // 启用线程池
        st = th_pool;
        // 创建epoll实例
        _efd = MyEpoll::CreateEpoll();
    }

    void LoopOnce()
    {
        std::cout << "方法树上socket数:" << fd_dispath.size() << std::endl;
        int n = MyEpoll::WaitEpoll(_efd, _epoll_event_array, max_num, 1000);
        std::cout << "事件数 n:" << n << std::endl;
        switch (n)
        {
        case -1:
            Logmessage(ERROR, "epoll wait fail");
            break;
        case 0:
            // Logmessage(INFO, "timeout");
            break;
        default:
            for (int i = 0; i < n; i++)
            {
                int fd = _epoll_event_array[i].data.fd;
                uint32_t revent = _epoll_event_array[i].events;
                int timefd = fd_dispath[fd]->_timefd;
                // 重新设置：一定是客户端来触发; lis_socket触发不行
                if (fd == fd_dispath[fd]->_fd && fd != _server_listen_socket)
                {
                    MyTimefd::SetTime(timefd, FIRST_TIMEOUT, TIMEOUT);
                    Logmessage(INFO, "socket %d 触发事件 定时器刷新", fd);
                }

                if (revent & (EPOLLERR | EPOLLHUP))
                {
                    if (is_exit_func_tree(fd))
                    {
                        std::cout << "debug: EPOLLERR" << std::endl;
                        ConnectData *tmp = fd_dispath[fd];
                        tmp->_except_event(tmp);
                    }
                }

                if (revent & EPOLLOUT)
                {
                    if (is_exit_func_tree(fd))
                    {
                        ConnectData *tmp = fd_dispath[fd];
                        std::thread from_out(tmp->_write_event, tmp);
                        from_out.detach();
                    }
                }

                if (revent & EPOLLIN)
                {
                    // 文件描述符方法树中查找, 并执行方法
                    if (is_exit_func_tree(fd))
                    {
                        ConnectData *tmp = fd_dispath[fd];
                        tmp->_read_event(tmp);
                    }
                }

                if (revent & (EPOLLIN | EPOLLERR | EPOLLET))
                    std::cout << "event true " << std::endl;
            }
            break;
        }
    }

    bool is_exit_func_tree(int data)
    {
        return fd_dispath.find(data) != fd_dispath.end();
    }

    void work_begin()
    {
        MySocket::listen_run(_server_listen_socket);
        MyEpoll::SetSocketNoHang(_server_listen_socket); // 为epollET设置非阻塞

        ConnectData *listen_connection = new ConnectData(_server_listen_socket);
        listen_connection->SetCallBack(this,
                                       std::bind(&webserver::AddSocketoEpoll, this, std::placeholders::_1),
                                       nullptr, nullptr);

        // 将listen_socket以ET模式添加
        MyEpoll::CtlEpoll(_efd, _server_listen_socket, EPOLL_CTL_ADD, EPOLLIN | EPOLLET); // epollET，只报告一次
        fd_dispath.insert({_server_listen_socket, listen_connection});
        while (1)
        {
            LoopOnce();
        }
    }

    // 普通socket设置方法表
    void AddSocketoEpoll(ConnectData *it)
    {
        sockaddr_in cline;
        uint16_t cline_prot;
        int actual_socket = MySocket::accept_run(_server_listen_socket, &cline, &cline_prot);
        if (actual_socket != -1)
        {
            Logmessage(INFO, "connect success 标识符:%d", actual_socket);
        }

        MyEpoll::SetSocketNoHang(actual_socket);
        // 将普通socket加入到epoll数，fd方法对应树
        ConnectData *socket_tmp = new ConnectData(actual_socket);
        socket_tmp->SetCallBack(this,
                                std::bind(&webserver::Connect_func_t, this, std::placeholders::_1),
                                std::bind(&webserver::write_func_t, this, std::placeholders::_1),
                                std::bind(&webserver::Except_func_t, this, std::placeholders::_1));
        MyEpoll::CtlEpoll(_efd, actual_socket, EPOLL_CTL_ADD, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLET);

        // 开始载入计时器，如果触发读事件，或者写事件，刷新时间
        //  创建定时器实例
        int timefd = MyTimefd::CreateTimefd();
        MyEpoll::SetSocketNoHang(timefd);
        MyTimefd::SetTime(timefd, FIRST_TIMEOUT, TIMEOUT);
        // 定时器的ConnectData是管理对象的描述符
        ConnectData *time_tmp = new ConnectData(actual_socket);
        time_tmp->SetCallBack(nullptr, std::bind(&webserver::TimeOut_func_t, this, std::placeholders::_1),
                              nullptr, nullptr);

        time_tmp->_timefd = timefd;
        socket_tmp->_timefd = timefd;
        MyEpoll::CtlEpoll(_efd, timefd, EPOLL_CTL_ADD, EPOLLIN | EPOLLET);

        // std::cout << "debug" << std::endl;
        if (!is_exit_func_tree(actual_socket))
        {
            fd_dispath.insert({actual_socket, socket_tmp});
            fd_dispath.insert({timefd, time_tmp});
        }
    }

    void Connect_func_t(ConnectData *it)
    {
        int fd = it->_fd;
        int timefd = it->_timefd;
        Logmessage(INFO, "epoll socket set %d", fd);
        if (st->Get_queue_task_reserver_size() < QUEUE_TASK_NUM)
        {
            std::cout << "task push" << std::endl;
            Thread_Task *task = new Thread_Task(fd, fd_dispath[fd]);
            st->push(task);
            st->swap_queue(QUEUE_TASK_NUM);
        }
    }

    void TimeOut_func_t(ConnectData *it)
    {
        int order_fd = it->_fd;
        Logmessage(INFO, "socket %d timeout", order_fd);
        Except_func_t(it);
    }

    void write_func_t(ConnectData *it)
    {
        int socket = it->_fd;
        Logmessage(INFO, "write once%d", socket);
        if (it->sendbuff.size() == 0)
            return;
        Logmessage(INFO, "epoll write %d", socket);
        while (1)
        {
            int ret = send(socket, it->sendbuff.c_str(), it->sendbuff.size(), 0);
            if (ret == -1)
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 发送缓冲区满了，等待下次再发
                else if (errno == EINTR)
                    continue;
                else
                    return;
            }
            else if (ret <= it->sendbuff.size())
            {
                it->sendbuff.erase(0, ret);
                if (it->sendbuff.empty())
                    break;
            }
        }

        if (it->sendbuff.empty())
        {
            Logmessage(INFO, "剩余数据发送完毕");
            Except_func_t(fd_dispath[socket]);
            std::cout << "断开 " << socket << std::endl;
        }
    }

    void Except_func_t(ConnectData *it)
    {
        int fd = it->_fd;
        int _timefd = it->_timefd;
        Logmessage(INFO, "Epoll Except %d", fd);
        // 删除该普通socket的epoll关心
        MyEpoll::DelEpoll(_efd, fd);
        // 删除 socket 对应 处理方法树中
        delete fd_dispath[fd];
        fd_dispath.erase(fd);

        // 删除该timeout socket的epoll关心
        MyEpoll::DelEpoll(_efd, _timefd);
        // 删除 socket 对应 处理方法树中
        delete fd_dispath[_timefd];
        fd_dispath.erase(_timefd);
        close(fd);
        close(_timefd);
    }
};

webserver *webserver::static_webserver = nullptr;
std::mutex webserver::_mutex;

bool MyEpoll::EnableReadWrite(ConnectData *con, bool read, bool write)
{
    uint32_t tmp = EPOLLET | EPOLLERR;
    if (read)
        tmp |= EPOLLIN;
    if (write)
        tmp |= EPOLLOUT;

    epoll_event room;
    room.data.fd = con->_fd;
    room.events = tmp;

    Logmessage(INFO, "socket %d 重新设置 write %d", con->_fd, write);

    if (epoll_ctl(con->_websever->GetEfd(), EPOLL_CTL_MOD, con->_fd, &room) == -1)
    {
        Logmessage(ERROR, "EnableReadWrite fail %s", strerror(errno));
        exit(3);
    }
    return true;
}

void *Thread_Task::operator()()
{
    HttpWork it(socket, _con);
    if (STOP == 1)
    {
        Logmessage(WARN, "请求读取异常,取消连接");
        _con->_websever->Except_func_t(_con);
        std::cout << "断开 " << socket << std::endl;
        return nullptr;
    }
    it.PaseHttpRequest();
    it.PaseURL();

    it.BuildHttpRespose();
    if (STOP == 1)
    {
        Logmessage(WARN, "send 异常");
    }

    // send未发送完成,等待再发送
    if (STOP == -1)
    {
        // 等待重发，先不关闭close
    }
    else
    {
        MyEpoll::EnableReadWrite(_con, true, false);
        // 正常等待超时或重新触发事件；异常退出
    }
}

#endif
